#include "stdafx.h"
#include <assert.h>
#include <shellapi.h>

static LPCTSTR kLinkIsVisitedProperty = L"LinkIsVisited";
static LPCTSTR kLinkIsHighlightedProperty = L"LinkIsHighlighted";

//---------------------------------

static
LRESULT CALLBACK HyperlinkWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

void RegisterHyperlinkControlClass(HINSTANCE inInstance)
{
	static bool wasRegistered = false;
	if (!wasRegistered)
	{
		WNDCLASS wc = { 0 };
		wc.style = 0;
		wc.lpfnWndProc = &HyperlinkWindowProc;
		wc.cbWndExtra = DLGWINDOWEXTRA;
		wc.hInstance = inInstance;
		wc.hCursor = ::LoadCursor(0, IDC_HAND);
		wc.hbrBackground = HBRUSH(COLOR_3DFACE + 1);
		wc.lpszClassName = L"HyperlinkControl";

		ATOM atom = ::RegisterClass(&wc);
		assert(0 != atom);
		wasRegistered = true;
	}
}

//---------------------------------------------------------------
static
void Draw(HWND inControl, const PAINTSTRUCT& ps)
{
	if (ps.fErase)
	{
		HBRUSH brush = ::GetSysColorBrush(COLOR_3DFACE);
		::FillRect(ps.hdc, &ps.rcPaint, brush);
	}

	static const int kMaxTextLen = 200;
	TCHAR buf[kMaxTextLen];
	if (int len = ::GetWindowText(inControl, buf, kMaxTextLen))
	{
		auto isVisited = ::GetProp(inControl, kLinkIsVisitedProperty);
		auto isHighlighted = ::GetProp(inControl, kLinkIsHighlightedProperty);

		DWORD textColor = isVisited ? RGB(255, 70, 50) : RGB(50, 70, 255);

		HWND parent = ::GetParent(inControl);
		assert(0 != parent);
		if (HFONT font = (HFONT)::SendMessage(parent, WM_GETFONT, 0, 0))
		{
			::SetTextColor(ps.hdc, textColor);
			::SetBkMode(ps.hdc, TRANSPARENT);

			if (isHighlighted)
			{
				LOGFONT lf;
				if (sizeof(lf) == ::GetObject(font, sizeof(lf), &lf))
				{
					lf.lfUnderline = true;
					if (HFONT f1 = ::CreateFontIndirect(&lf))
					{
						auto oldFont = ::SelectObject(ps.hdc, f1);
						::TextOut(ps.hdc, 0, 0, buf, len);
						::SelectObject(ps.hdc, oldFont);
						::DeleteObject(f1);
					}
				}
			}
			else
			{
				::SelectObject(ps.hdc, font);	
				::TextOut(ps.hdc, 0, 0, buf, len);
			}
		}
	}
}

static void SetHighlighted(HWND inControl, bool inHighlighted)
{
	HANDLE highlighted = (HANDLE)inHighlighted;
	auto oldHighlighted = ::GetProp(inControl, kLinkIsHighlightedProperty);
	::SetProp(inControl, kLinkIsHighlightedProperty, highlighted);
	if (highlighted != oldHighlighted)
	{
		::InvalidateRect(inControl, NULL, true);
	}
}

static void LaunchBrowser(HWND inControl)
{
	static const int kMaxTextLen = 200;
	TCHAR buf[kMaxTextLen];
	if (int len = ::GetWindowText(inControl, buf, kMaxTextLen))
	{
		HWND dialog = ::GetParent(inControl);
		::ShellExecute(dialog, L"open", buf, nullptr, nullptr, SW_SHOWNORMAL);
	}
}

// ------------------------------------------------------------
static
LRESULT CALLBACK HyperlinkWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	LRESULT result = 0;
	switch (uMsg)
	{
	case WM_PAINT:
		{
			PAINTSTRUCT ps;
			HDC dc = ::BeginPaint(hwnd, &ps);
			Draw(hwnd, ps);
			::EndPaint(hwnd, &ps);
		}
		break;
	case WM_SETCURSOR:
		SetHighlighted(hwnd, (hwnd == HWND(wParam)));
		result = ::DefWindowProc(hwnd, uMsg, wParam, lParam);
		break;
	case WM_POINTERLEAVE:
		SetHighlighted(hwnd, false);
		break;
	case WM_LBUTTONUP:
		::SetProp(hwnd, kLinkIsVisitedProperty, (HANDLE)true);
		::InvalidateRect(hwnd, nullptr, true);
		LaunchBrowser(hwnd);
		break;
	default:
		result = ::DefWindowProc(hwnd, uMsg, wParam, lParam);
		break;
	}
	return result;
}


